﻿using NLog;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Common;
using System.Data.Entity;
using System.Data.Entity.Infrastructure.Interception;
using System.Linq;
using System.Text;

namespace EFDemo
{
    public class EfSample
    {
        static EfSample()
        {
            Database.SetInitializer<TestDbContext>(null);
            DbInterception.Add(new NLogInterceptor());
        }

        public static void TestUpdateAndDelete()
        {

            var node = new NodeEntity { ID = 4, NodeOrder = 10, NodeName = "Change NodeName" };

            using (var context = new TestDbContext("localSql"))
            {
                context.Configuration.AutoDetectChangesEnabled = false;
                context.Database.Log = Console.WriteLine;
                node.NodeName = "change node" + node.NodeName;
                node.CreatedON = DateTime.Now;
                context.Entry<NodeEntity>(node).State = EntityState.Modified;
                context.SaveChanges();
            }

            using (var context = new TestDbContext("localSql"))
            {
                context.Configuration.AutoDetectChangesEnabled = false;
                context.Database.Log = Console.WriteLine;
                node.NodeName = "change node" + node.NodeName;
                node.CreatedON = DateTime.Now;
                context.Entry<NodeEntity>(node).State = EntityState.Deleted;
                context.SaveChanges();
            }
        }

        public static void TestInsert()
        {
            using (var context = new TestDbContext("localSql"))
            {
                context.Configuration.AutoDetectChangesEnabled = false;
                context.Database.Log = Console.WriteLine;
                var n = new NodeEntity() { NodeName = "NodeName", CreatedON = DateTime.Now, NodeOrder = 1, Destinations = new List<NodeDestination>() };
                n.Destinations.Add(new NodeDestination() { CreatedOn = DateTime.Now, DestinationAD = "global-test" });

                context.Nodes.Add(n);
                var rowCount = context.SaveChanges();
                Console.WriteLine("RowCount: {0}", rowCount);
            }
        }

        public static void TestQuery()
        {
            using (var context = new TestDbContext("localSql"))
            {
                context.Configuration.AutoDetectChangesEnabled = false;
                context.Database.Log = Console.WriteLine;



                var nodes = context.Nodes.AsNoTracking().Include(it => it.Destinations);
                foreach (var item in nodes)
                {
                    Console.WriteLine("node = {0}", item.NodeName);
                    item.Destinations.ForEach(d =>
                    {
                        Console.WriteLine("\tAD: {0}", d.DestinationAD);
                    });
                }

                //var nodes2 = context.Database.SqlQuery<NodeEntity>("select * from T_PROC_Node WHERE Id >= 4");
                //foreach (var item in nodes2)
                //{
                //    Console.WriteLine("node = {0}", item.NodeName);
                //    if (item.Destinations != null)
                //    {
                //        item.Destinations.ForEach(d =>
                //        {
                //            Console.WriteLine("\tAD: {0}", d.DestinationAD);
                //        });
                //    }
                //    else
                //    {
                //        Console.WriteLine("\tDestinations is empty");
                //    }

                //}

                //var jobConfigNew = context.Database.SqlQuery(typeof(JobConfigNew), "select * from JobConfig");

                //foreach (JobConfigNew item in jobConfigNew)
                //{
                //    Console.WriteLine(item.DllPath);
                //}

            }

            using (var context = new TestDbContext("localSql"))
            {
                var nodes = context.Nodes.AsNoTracking().Include(it => it.Destinations).ToList();
                foreach (var item in nodes)
                {
                    Console.WriteLine("node = {0}", item.NodeName);
                    item.Destinations.ForEach(d =>
                    {
                        Console.WriteLine("\tAD: {0}", d.DestinationAD);
                    });
                }
            }
        }
    }

    public class NLogInterceptor : IDbCommandInterceptor
    {
        private static readonly Logger Logger = LogManager.GetCurrentClassLogger();

        private void LogIfNonAsync<TResult>(DbCommand command, DbCommandInterceptionContext<TResult> context)
        {
            if (!context.IsAsync)
            {
                Logger.Warn("non-async command used: {0}", command.CommandText);
            }
        }

        private void LogIfError<TResult>(DbCommand command, DbCommandInterceptionContext<TResult> context)
        {
            if (context.Exception != null)
            {
                Logger.Error("Command {0} failed with exception {1}",
                    command.CommandText, context.Exception);
            }
        }

        public void NonQueryExecuted(System.Data.Common.DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
        {
            LogIfError(command, interceptionContext);
        }

        public void NonQueryExecuting(System.Data.Common.DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
        {
            LogIfNonAsync(command, interceptionContext);
        }

        public void ReaderExecuted(System.Data.Common.DbCommand command, DbCommandInterceptionContext<System.Data.Common.DbDataReader> interceptionContext)
        {
            LogIfError(command, interceptionContext);
        }

        public void ReaderExecuting(System.Data.Common.DbCommand command, DbCommandInterceptionContext<System.Data.Common.DbDataReader> interceptionContext)
        {
            LogIfNonAsync(command, interceptionContext);
        }

        public void ScalarExecuted(System.Data.Common.DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
        {
            LogIfError(command, interceptionContext);
        }

        public void ScalarExecuting(System.Data.Common.DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
        {
            LogIfNonAsync(command, interceptionContext);
        }
    }

    [Table("JobConfig")]
    public class JobConfigEntity
    {
        public int ID { get; set; }
        public string JobName { get; set; }
        public string DllPath { get; set; }
    }

    public class JobConfigNew
    {
        public int ID { get; set; }
        public string JobName { get; set; }
        public string DllPath { get; set; }
    }

    [Table("T_PROC_Node")]
    public class NodeEntity
    {
        [Key]
        public int ID { get; set; }
        public string NodeName { get; set; }
        public int NodeOrder { get; set; }
        public DateTime? CreatedON { get; set; }
        public List<NodeDestination> Destinations { get; set; }

    }

    [Table("T_PROC_Node_Destination")]
    public class NodeDestination
    {
        [Key]
        public int ID { get; set; }
        public int? NodeID { get; set; }
        public NodeEntity Node { get; set; }
        public string DestinationAD { get; set; }
        public DateTime? CreatedOn { get; set; }
    }

    public class TestDbContext : DbContext
    {
        public TestDbContext(string nameOrConnectionString)
            : base(nameOrConnectionString)
        {
        }

        public DbSet<JobConfigEntity> JobConfigs { get; set; }
        public DbSet<NodeEntity> Nodes { get; set; }
        public DbSet<NodeDestination> Destinations { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            //modelBuilder.Conventions.Remove<System.Data.Entity.ModelConfiguration.Conventions.PluralizingTableNameConvention>();

            modelBuilder.Entity<NodeEntity>().ToTable("T_PROC_Node").HasMany(n => n.Destinations).WithOptional(it => it.Node);

            base.OnModelCreating(modelBuilder);
        }
    }
}
